package com.example.wujinapi.comm.wechat;

import com.example.wujinapi.comm.R;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.core.io.ClassPathResource;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.security.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

@Slf4j
public class WxTools {

    /**
     * XML格式字符串转换为Map
     *
     * @param strXML XML字符串
     * @return XML数据转换后的Map
     * @throws Exception
     */
    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilder documentBuilder = WXPayXmlUtil.newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            throw ex;
        }

    }

    /**
     * 将Map转换为XML格式的字符串
     *
     * @param data Map类型数据
     * @return XML格式的字符串
     * @throws Exception
     */
    public static String mapToXml(Map<String, String> data) throws Exception {
        org.w3c.dom.Document document = WXPayXmlUtil.newDocument();
        org.w3c.dom.Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key: data.keySet()) {
            String value = data.get(key);
            if (value == null) {
                value = "";
            }
            value = value.trim();
            org.w3c.dom.Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
        try {
            writer.close();
        }
        catch (Exception ex) {
        }
        return output;
    }



    //length用户要求产生字符串的长度
    public static String getRandomString(int length){
        String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random=new Random();
        StringBuffer sb=new StringBuffer();
        for(int i=0;i<length;i++){
            int number=random.nextInt(62);
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }



    //HMACSHA256 加密
    public static String HMACSHA256(String data, String key) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1,3));
        }
        return sb.toString().toUpperCase();
    }



    /**
     * 加载保存证书
     *
     * @param mchId    商户ID
     * @param certPath 证书位置
     * @throws Exception
     */
    public static KeyStore LoadCert(String mchId, String certPath) throws Exception {
        // 证书密码，默认为商户ID
        String key = mchId;
        // 证书的路径
        ClassPathResource resource = new ClassPathResource(certPath);
        // 指定读取证书格式为PKCS12
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        // 读取本机存放的PKCS12证书文件
        InputStream instream =resource.getInputStream();
        try {
            // 指定PKCS12的密码(商户ID)
            keyStore.load(instream, key.toCharArray());
        } catch (Exception e) {
            log.error("keyStoreException", e);
        } finally {
            instream.close();
        }
        System.out.println("keyStore的值:"+keyStore);
        return keyStore;
    }

    /**
     * 请求微信平台证书工具
     */
    //String schema = "WECHATPAY2-SHA256-RSA2048";
    //String url="https://api.mch.weixin.qq.com/v3/certificates";
    //HttpUrl httpurl = HttpUrl.parse(url);
    //static  Map<String, String> wx = sysSettingService.getMap("wx");
    public static String getToken(Map<String, String> wxConfig,String method, HttpUrl url, String body) throws Exception {
        String yourMerchantId=wxConfig.get("pay_service_num");
        log.info("method:"+method+",url:"+url+"yourMerchantId:"+yourMerchantId);
        String yourCertificateSerialNo="13DA2E1A0D930E87123B4CEC8ADCC4A11CB708EE";  //商户平台API证书
        String nonceStr = WxTools.getRandomString(16);
        long timestamp = System.currentTimeMillis() / 1000;
        String message = buildMessage(method, url, timestamp, nonceStr, body);
        log.info("构建message:"+message);
        String signature = sign(message.getBytes("utf-8"));
        return "mchid=\"" + yourMerchantId + "\","
                + "nonce_str=\"" + nonceStr + "\","
                + "timestamp=\"" + timestamp + "\","
                + "serial_no=\"" + yourCertificateSerialNo + "\","
                + "signature=\"" + signature + "\"";
    }


   /* public static String GetAuthorization(Map<String, String> wxConfig,String Url) throws Exception {
        *//*Map<String,String> wxConfig = wxConfig.getMap("wx"); //获取数据库微信配置
        String schema = "WECHATPAY2-SHA256-RSA2048";
        HttpUrl httpurl = HttpUrl.parse(Url);
        String Token=schema+" "+WxTools.getToken(wxConfig,"GET",httpurl,"");*//*
    }*/


    /**
     * 签名
     */
    public static String sign(byte[] message) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        /*//创建KeyPair生成器对象
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        //初始化键对生成器
        keyPairGen.initialize(2048);
        //生成键对
        KeyPair pair = keyPairGen.generateKeyPair();
        //从键对获取私钥
        PrivateKey privKey = pair.getPrivate();*/
        //使用商户的秘钥进行签名加密
        PrivateKey privateKey = WxSignUtils.getApiPrivateKey(WxConstants.API_CLIENT_SERVER_KEY_PATH);
        sign.initSign(privateKey);
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     * 构造信息体
     */
    public static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) throws Exception {
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
            canonicalUrl += "?" + url.encodedQuery();
        }
        return method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";  //对于下载证书的接口来说，请求报文主体是一个空串。
    }

    /**
     * 解密证书和回调报文解密
     * @param ApiV3Key 用户秘钥 要转换位字节流
     * @param associatedData 附加数据包
     * @param ciphertext 加密文本
     */
    public static String decryptToString(byte[] ApiV3Key,byte[] associatedData, byte[] nonce, String ciphertext)
            throws GeneralSecurityException, IOException {
        final int KEY_LENGTH_BYTE = 32;
        final int TAG_LENGTH_BIT = 128;

        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            SecretKeySpec key = new SecretKeySpec(ApiV3Key, "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);

            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);

            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");

        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }


    /**
     * 小程序code换取用户openid
     */
    public static JSONObject GetUserOpenidAndSessionKey(String wxLoginCode){
        String wxAppid=WxConfig.App_Key;  //公众号appid
        String wxSecret= WxConfig.App_Secret; //公众号秘钥
        String result = sendGet("https://api.weixin.qq.com/sns/jscode2session",
                "appid=" + wxAppid + //小程序APPID
                        "&secret="+ wxSecret + //小程序秘钥
                        "&js_code="+ wxLoginCode + //前端传来的code
                        "&grant_type=authorization_code");
        //System.out.println("请求openid返回结果"+result);
        //log.info("请求openid返回结果"+result);
        JSONObject jsonObject = JSON.parseObject(result);
        //System.out.println("对象===>:"+jsonObject);
        return jsonObject;
    }



    public static String sendGet(String url, String param) {
        String result = "sendGet请求失败";
        CloseableHttpClient httpClient = null;
        try {
            httpClient= HttpClients.createDefault();
            //请求URL
            URIBuilder uriBuilder = new URIBuilder(url + "?" + param);
            //完成签名并执行请求
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            CloseableHttpResponse response = httpClient.execute(httpGet);
            /*System.out.println("请求url:"+uriBuilder.build()+",请求openid回应:"+EntityUtils.toString(response.getEntity()));*/
            if (response.getStatusLine().getStatusCode() == 200) {
                return EntityUtils.toString(response.getEntity());
            }
            /*JSONObject json = JSONUtil.parseObj(EntityUtils.toString(response.getEntity()));
            return json.get("openid").toString();*/
        } catch (Exception e) {
            System.out.println("【发送GET请求出现异常】！" + e);
            e.printStackTrace();
        }
        return result;
    }


    /**
    * 请求url POST
    * */
    public static R RequestUrl (String url) {
        CloseableHttpClient httpClient = null;
        try {
            //请求URL
            HttpPost httpPost = new HttpPost(url);
            //完成签名并执行请求
            CloseableHttpResponse response = httpClient.execute(httpPost);
            System.out.println("服务器回应:"+response+"，编码:"+EntityUtils.toString(response.getEntity(), "UTF-8"));
            //如果状态码为200,就是正常返回
            if (response.getStatusLine().getStatusCode() == 200) {
                return R.ok(EntityUtils.toString(response.getEntity(), "UTF-8"));
            }
            JSONObject json = JSON.parseObject(EntityUtils.toString(response.getEntity(), "UTF-8"));
            return R.error(json.get("message") + "");
        } catch (Exception e) {
            e.printStackTrace();
            return R.error();
        } finally {
            if (httpClient != null) {
                try {
                    httpClient.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


} //主体类 End
